<?php
//https://open.sandpay.com.cn/product/detail/43311/43795/
namespace jy_sandpay;
defined('BY_JYA') or exit('error');
class pay extends \plugin{
    public function __construct($setting=array()){
        parent::__construct('jy_sandpay');
        $setting=uni_setting('pay');
        if(!$setting['sandpay_mid'] || !$setting['sandpay_key'] || !$setting['sandpay_private']  || !$setting['sandpay_public']){
            exi('商户号，私钥证书密码，私钥文件，公钥文件都必须填写','error');
        }
        if($_SESSION['auth_wxid']){
            $auth_appid=pdo_getcolumn('core_account_wechat',array('id'=>$_SESSION['auth_wxid']),'appid');
        }
        $wechat_appid=$auth_appid?:$setting['wechat_appid'];
        if(!$wechat_appid){
            $wechat_appid=pdo_getcolumn('core_account_wechat',array('uniacid'=>$_SESSION['uniacid'],'isdefault'=>1),'appid');
            if(!$wechat_appid){
                $wechat_appid=pdo_getcolumn('core_account_wechat',array('uniacid'=>$_SESSION['uniacid']),'appid');
            }
        }
        $this->config=array(
            'appid'=>$wechat_appid,
            'sandpay_mid'=>$setting['sandpay_mid'],
            'sandpay_key'=>$setting['sandpay_key'],
            'sandpay_private'=>ROOT_AT.$setting['sandpay_private'],
            'sandpay_public'=>ROOT_AT.$setting['sandpay_public'],
        );
    }
    //创建订单
    public function create($pay_log,$paytype=''){
        # 支付设置
        if($paytype=='wechat' && $_SESSION['container']=='wechat'){
            $paytype='sand_wx';

        }
        if($paytype=='alipay'){
            $paytype='sand_alipay';
        }
        if($paytype=='union_online'){
            $paytype='sand_h5';
        }
        if($paytype=='sand_wx'){
            if(!$this->config['appid']) {
                exi('未绑定微信公众号，不能使用微信支付！', 'error');
            }
            $expend=array(
                'subAppid'=>$this->config['appid'],
                'userId'=>$pay_log['openid']?:$_SESSION['openid'],
            );
        }elseif($paytype=='sand_alipay'){
            $expend=array(
                'userId'=>$_SESSION['openid'],
            );
        }
        $totalAmount=sprintf("%012d", $pay_log['fee']*100);
        $payment_params = array(
            'orderCode'=> $pay_log['tid']?:$pay_log['ordersn'],

            'totalAmount'=>$totalAmount,
            'subject'=>$pay_log['title'],
            'body'=>$pay_log['title'],
            'clientIp'=>CLIENT_IP,
            'payExtra'=>$expend,
            'notifyUrl'=>$pay_log['notify_url']?:SITEROOT .'addons/jy_sandpay/notify.php',
            'frontUrl'=>$pay_log['notify_url']?:SITEROOT .'addons/jy_sandpay/notify.php',
        );
        //print_r($payment_params);exit;
        # 发起支付
        $apiurl='https://cashier.sandpay.com.cn/gateway/api/order/pay';
        $res=$this->request($apiurl,$payment_params,'sandpay.trade.pay');
        # 对支付结果进行处理
        if($res['error_code']!='000000'){
            exi('支付失败:'.$res['error_msg'].',错误码：'.$res['error_code'],'error');
        }
        $expend=$res['expend'];
        if($paytype=='sand_alipay'){
            return $expend['pay_info'];
        }elseif($paytype=='sand_wx'){
            $package=array(
                'appId'=>$res['appId'],
                'timeStamp'=>"".TIMESTAMP,
                'package'=>'prepay_id='.$res['tradeNo'],
                'signType'=>'MD5',
                'nonceStr'=>random(8),
            );
            $package['paySign']=cfc('wechat_pay')->md5_sign($package);
            return $package;
        }elseif($paytype=='sand_h5'){
            return $expend['pay_url'];
        }
        return $res;
    }
    /*
        发起接口请求
    */
    private function request($api_url,$package,$method='',$msg='',$isres=false){
        $postData = $this->postData($method,$package);
        $res=$this->httpPost($api_url, $postData);
        //print_r($res);exit;
        if(is_error($res)){
            if($msg){
                return error($res['message']);
            }else{
                exi($res['message'].'，请联系管理员设置好参数！','error');
            }
        }
        $res = $this->parseResult($res);
        //print_r($res);exit;
        $res=json_decode($res['data'],true);
        if($res['head']['respCode']!='000000'){
            if($isres){
                return $res;
            }
            if($msg){
                return error($res['head']['respMsg']);
            }else{
                exi($res['head']['respMsg'].'，错误码：'.$res['head']['respCode'],'error');
            }
        }
        // 验签 & 返回结果
        $verifyFlag = $this->verify($res['data'], $res['sign']);
        return $res['body'];
    }
    // 参数
    public function postData($method,$package){
        if($package['payMode']=='sand_wx'){
            if($_SESSION['wxapp_id']){
                $productId='00002021';
            }else{
                $productId='00002020';
            }
        }elseif($package['payMode']=='sand_alipay'){
            if($_SESSION['aliapp_id']){
                $productId='00002023';
            }else{
                $productId='00002022';
            }
        }elseif($package['payMode']=='sand_h5'){
            $productId='00000008';
        }
        $data = array(
            'head' => array(
                'version'     => '1.0',
                'method'      => $method,
                'productId'   => $productId,
                'accessType'  => '1',
                'mid'         => $this->config['sandpay_mid'],
                'plMid'       => '',
                'channelType' => '07',
                'reqTime'     => date('YmdHis', time()),
            ),
            'body' => $package,
        );
        $postData = array(
            'charset'  => 'utf-8',
            'signType' => '01',
            'data'     => json_encode($data),
            'sign'     => $this->sign($data),
        );
        return $postData;
    }
    // curl. 发送请求
    public function httpPost($url, $params){
        if (empty($url) || empty($params)) {
            exi('请求参数错误！','error');
        }
        $params = http_build_query($params);
        try {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data  = curl_exec($ch);
            $err   = curl_error($ch);
            $errno = curl_errno($ch);
            if ($errno) {
                $msg = 'curl errInfo: ' . $err . ' curl errNo: ' . $errno;
                throw new \Exception($msg);
            }
            curl_close($ch);
            return $data;
        } catch (\Exception $e) {
            if ($ch) curl_close($ch);
            throw $e;
        }
    }
    // curl.解析返回数据
    protected function parseResult($result)
    {
        $arr      = array();
        $response = urldecode($result);
        $arrStr   = explode('&', $response);
        foreach ($arrStr as $str) {
            $p         = strpos($str, "=");
            $key       = substr($str, 0, $p);
            $value     = substr($str, $p + 1);
            $arr[$key] = $value;
        }

        return $arr;
    }
    // 公钥
    private function publicKey()
    {
        try {
            $file = file_get_contents($this->config['sandpay_public']);
            if (!$file) {
                exi('公钥文件读取有误','error');
            }
            $cert   = chunk_split(base64_encode($file), 64, "\n");
            $cert   = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
            $res    = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);
            if (!$detail) {
                exi('公钥文件解析有误','error');
            }
            return $detail['key'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // 私钥
    private function privateKey()
    {
        try {
            $file = file_get_contents($this->config['sandpay_private']);
            if (!$file) {
                exi('私钥文件读取有误','error');
            }
            if (!openssl_pkcs12_read($file, $cert, $this->config['sandpay_key'])) {
                exi('私钥密码错误','error');
            }
            return $cert['pkey'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // 私钥加签
    protected function sign($plainText)
    {
        $plainText = json_encode($plainText);
        try {
            $resource = openssl_pkey_get_private($this->privateKey());
            $result   = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);
            if (!$result) {
                exi('签名失败！','error');
            }
            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // 公钥验签
    public function verify($plainText, $sign)
    {
        $resource = openssl_pkey_get_public($this->publicKey());
        $result   = openssl_verify($plainText, base64_decode($sign), $resource);
        openssl_free_key($resource);
        if (!$result) {
            exi('签名验证未通过','error');
        }
        return $result;
    }
}